package wallet

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"net/http"
	"os"
	"path/filepath"
	"sync"

	"gitee.com/magic-mountain/sigs"
	"gitee.com/magic-mountain/utils"
	"gitee.com/magic-mountain/utils/crypto/aes"
	"gitee.com/magic-mountain/utils/crypto/md5"
	"gitee.com/magic-mountain/utils/rpc"
	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/go-jsonrpc/auth"
	"github.com/filecoin-project/go-state-types/crypto"
	"github.com/gorilla/mux"
	"github.com/syndtr/goleveldb/leveldb"

	//_ "gitee.com/magic-mountain/sigs/bls"
	_ "gitee.com/magic-mountain/sigs/secp"

	"golang.org/x/xerrors"
)

const (
	// KNamePrefix = "wallet-"
	// KList       = "asmb-chain-wallet-list"
	KDefault = "asmb-chain-wallet"
)

//type wallethash [20]byte
type Keys struct {
	Defaultkey *Key //当前签名key
	Keylist    []*Key
	list       map[string]*Key
}
type Wallet struct {
	password string
	keystore *leveldb.DB
	Keys     *Keys

	mutex sync.Mutex
}

func NewwalletCur(pwd string) (*Wallet, func(), error) {
	d, err := leveldb.OpenFile(filepath.Dir(os.Args[0]), nil)
	closer := func() {
		defer d.Close()
	}
	if err != nil {
		closer()
		return nil, nil, err
	}
	w, err := Newwallet(pwd, d)
	return w, closer, err
}

func Newwallet(pwd string, keystore *leveldb.DB) (*Wallet, error) {
	//address.CurrentNetwork = address.Mainnet
	var ws Keys = Keys{list: map[string]*Key{}}
	wallet := Wallet{password: pwd, keystore: keystore, Keys: &ws}
	wk, err := keystore.Get([]byte(KDefault), nil)
	if err != nil {
		return &wallet, nil
	}

	kest, err := wallet.restore(wk, pwd)
	if err != nil {
		return &wallet, nil
	}
	//fmt.Println(wk)

	wallet.Keys.Defaultkey = kest.Defaultkey
	wallet.Keys.Keylist = kest.Keylist
	for _, v := range wallet.Keys.Keylist {
		wallet.Keys.list[string(v.Address.Payload())] = v
	}
	// for k, _ := range ws.List {
	// 	fmt.Println(len(k))
	// }
	return &wallet, nil
}

func (w *Wallet) Init(ctx context.Context, typ crypto.SigType) (address.Address, error) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	k, err := GenerateKey(typ)
	if err != nil {
		return address.Undef, err
	}
	w.Keys.Defaultkey = k
	w.Keys.list = make(map[string]*Key)
	w.Keys.list[string(k.Address.Payload())] = k

	if _, err = w.store(); err != nil {
		return address.Undef, err
	}

	return k.Address, nil
}

func (w *Wallet) restore(abt []byte, pwd string) (*Keys, error) {
	jbt, err := aes.AesCBCDncrypt(abt, md5.MD5Hex(utils.Pointer2Bytes(pwd)))
	if err != nil {
		return nil, err
	}
	var keys_t Keys
	err = json.Unmarshal(jbt, &keys_t)
	if err != nil {
		return nil, err
	}

	return &keys_t, nil
}
func (w *Wallet) store() ([]byte, error) {
	list := make([]*Key, len(w.Keys.list))
	i := 0
	for _, v := range w.Keys.list {
		//w.Keys.Keylist
		list[i] = v
		i++
	}

	w.Keys.Keylist = list
	kjson, err := json.Marshal(w.Keys)
	if err != nil {
		return nil, err
	}
	aesbytes, err := aes.AesCBCEncrypt(kjson, md5.MD5Hex(utils.Pointer2Bytes(w.password)))
	if err != nil {
		return nil, err
	}
	if err := w.keystore.Put([]byte(KDefault), aesbytes, nil); err != nil {
		return nil, xerrors.Errorf("saving to keystore: %w", err)
	}
	return aesbytes, nil
}

func (w *Wallet) New(ctx context.Context, typ crypto.SigType) (address.Address, error) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	k, err := GenerateKey(typ)
	if err != nil {
		return address.Undef, err
	}
	//w.Keys.Defaultkey
	w.Keys.list[string(k.Address.Payload())] = k
	// for k, v := range w.Keys.list {
	// 	fmt.Println(len(k), v)
	// }
	//fmt.Println(len(w.Keys.list[string(k.Address.Payload())].Address.Payload()))
	// if string(k.Address.Payload()) == utils.Pointer2Str(k.Address.Payload()) {
	// 	fmt.Println([]byte(string(k.Address.Payload())), []byte(utils.Pointer2Str(k.Address.Payload())))
	// }
	if _, err = w.store(); err != nil {
		return address.Undef, err
	}
	return k.Address, nil
}
func (w *Wallet) List(ctx context.Context) ([]address.Address, error) {

	addrs := []address.Address{w.Keys.Defaultkey.Address}
	for _, value := range w.Keys.list {
		addrs = append(addrs, value.Address)
	}
	//	time.Sleep(time.Second * time.Duration(rand.Intn(10)))
	return addrs, nil
}
func (w *Wallet) Delete(ctx context.Context, address address.Address) error {
	w.mutex.Lock()
	defer w.mutex.Unlock()

	delete(w.Keys.list, string(address.Payload()))

	if w.Keys.Defaultkey.Address.String() == address.String() {
		for _, value := range w.Keys.list {
			w.Keys.Defaultkey = value
			break
		}
	}

	kjson, err := json.Marshal(w.Keys)
	if err != nil {
		return err
	}

	if err := w.keystore.Put([]byte(KDefault), kjson, nil); err != nil {
		return xerrors.Errorf("saving to keystore: %w", err)
	}
	return nil
}

func (w *Wallet) SignDefault(ctx context.Context, msg []byte) (*crypto.Signature, error) {

	if &w.Keys.Defaultkey.Address == &address.Undef {
		return nil, xerrors.Errorf("signing using key 'Defaultkey': not found")
	}

	return sigs.Sign(w.Keys.Defaultkey.Type, w.Keys.Defaultkey.PrivateKey, msg)
}
func (w *Wallet) Sign(ctx context.Context, address []byte, msg []byte) (*crypto.Signature, error) {

	if a, ok := w.Keys.list[utils.Pointer2Str(address)]; ok {
		return sigs.Sign(w.Keys.Defaultkey.Type, a.PrivateKey, msg)

	}
	addrjson, err := json.Marshal(address)
	return nil, xerrors.Errorf("signing using key 'address':%s,%v not found", addrjson, err)
}
func (w *Wallet) Verify(ctx context.Context, address []byte, msg []byte, sig *crypto.Signature) (bool, error) {
	return sigs.Verify(sig, address, msg) == nil, nil
}
func Verify(ctx context.Context, address []byte, msg []byte, sig *crypto.Signature) error {
	return sigs.Verify(sig, address, msg)
}
func (w *Wallet) Export(ctx context.Context) (string, error) {
	abytes, err := w.store()
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(abytes), nil
}
func (w *Wallet) Import(ctx context.Context, hexs string, pwd string) error {
	abt, err := hex.DecodeString(hexs)
	if err != nil {
		return err
	}
	keyt, err := w.restore(abt, pwd)
	if err != nil {
		return err
	}
	for _, v := range keyt.Keylist {
		w.Keys.list[string(v.Address.Payload())] = v
	}
	_, err = w.store()
	if err != nil {
		return err
	}
	return nil
}
func (w *Wallet) Default(ctx context.Context) address.Address {

	return w.Keys.Defaultkey.Address
}
func (w *Wallet) Setdefault(ctx context.Context, a address.Address) error {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.Keys.Defaultkey = w.Keys.list[a.String()]

	kjson, err := json.Marshal(w.Keys)
	if err != nil {
		return err
	}

	if err := w.keystore.Put([]byte(KDefault), kjson, nil); err != nil {
		return xerrors.Errorf("saving to keystore: %w", err)
	}
	return nil
}

func (w *Wallet) StratrpcServer(url string) error {

	mux := mux.NewRouter()

	//	rpcServer := jsonrpc.NewServer()

	var out IwalletStruct
	// auth.PermissionedProxy(rpc.AllPermissions, rpc.DefaultPerms, w, &out.Internal)
	// rpcServer.Register("wallet", &out)

	mux.Handle("/wallet/v0", rpc.RegisterRpc(w, &out.Internal, &out, "wallet"))

	ah := &auth.Handler{
		Verify: rpc.AuthVerify,
		Next:   mux.ServeHTTP,
	}

	return http.ListenAndServe(url, ah)
}

//type helloHandler struct{}

// func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 	w.Write([]byte("Hello, world!"))
// }
// func main() {
// 	var err error
// 	http.Handle("/", &helloHandler{})
// 	err = http.ListenAndServe(":8083", nil)
// 	// IPv4 或 IPv6
// 	err = http.ListenAndServe("[2604:180:3:dd3::276e]:8083", nil)
// 	// 具体指定，仅 IPv6
// 	err = ListenAndServe(":8083", nil)
// 	// 重构 ListenAndServe 函数
// 	if err != nil {
// 		fmt.Println(err)
// 	}
// }

//type tcpKeepAliveListener struct{ *net.TCPListener }

// func ListenAndServe(addr string, handler http.Handler) error {
// 	srv := &http.Server{Addr: addr, Handler: handler}
// 	addr = srv.Addr
// 	if addr == "" {
// 		addr = ":http"
// 	}
// 	ln, err := net.Listen("tcp6", addr)
// 	// 仅指定 IPv6
// 	if err != nil {
// 		return err
// 	}
// 	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
// }

// func GetRpcClient(url string) (*IwalletStruct, func(), error) {

// 	var out IwalletStruct
// 	closer, err := jsonrpc.NewClient(context.Background(), "ws://"+url+"/wallet/v0", "wallet", &out.Internal, rpc.AuthHeader())
// 	if err != nil {
// 		return nil, nil, err
// 	}

// 	return &out, closer, nil
// }

// func GetWalletRpcClient(t, url string) (*IwalletStruct, func(), error) {

// 	var out IwalletStruct
// 	closer, err := jsonrpc.NewClient(context.Background(), "ws://"+url+"/wallet/v0", "wallet", &out.Internal, rpc.AuthHeaderWtoken(t))
// 	if err != nil {
// 		return nil, nil, err
// 	}

// 	return &out, closer, nil
// }
